package com.liruo.learn.spring.security.configuration;

import com.liruo.learn.spring.security.auth.service.AuthenticationUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;

/**
 * @Author:liruo
 * @Date:2023-05-16-11:36:21
 * @Desc 认证配置
 */
@Slf4j
@Configuration
@EnableWebSecurity
public class AuthenticationServerConfiguration extends WebSecurityConfiguration implements ApplicationContextAware {
    @Resource
    private AuthenticationUserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static void deleteParentAuthenticationManager(HttpSecurity http, ApplicationContext applicationContext) {
        try {
            AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
            authenticationManagerBuilder.parentAuthenticationManager(null);

            AuthenticationConfiguration authenticationConfiguration = applicationContext.getBean(AuthenticationConfiguration.class);
            if(authenticationConfiguration != null){
                Field parentAuthenticationManager = ReflectionUtils.findField(AuthenticationConfiguration.class, "authenticationManager");
                ReflectionUtils.makeAccessible(parentAuthenticationManager);
                parentAuthenticationManager.set(authenticationConfiguration, null);
                Field authenticationManagerInitialized = ReflectionUtils.findField(AuthenticationConfiguration.class, "authenticationManagerInitialized");
                ReflectionUtils.makeAccessible(authenticationManagerInitialized);
                authenticationManagerInitialized.set(authenticationConfiguration, true);
            }else{
                log.info("completely forbidden parentAuthenticationManager fail, for has not the bean of authenticationConfiguration");
            }
        } catch (Exception e) {
            log.info("deleteParentAuthenticationManager fail", e);
        }
    }

    /**
     * http.authorizeHttpRequests(): 这是过时的, http.authorizeRequests()配置更灵活
     * requiresChannel()是配置走HTTPS, authorizeHttpRequests()配置的不会走HTTPS
     * 配置SecurityFilterChain的路径: 默认是/**
     * mvcMatcher() / antMatcher() / regexMatcher() / requestMatcher() / requestMatchers()
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        AuthenticationServerConfiguration.deleteParentAuthenticationManager(http, applicationContext);

        http.authorizeRequests()
                .antMatchers(
                        "/test/any/**",
                        "/auth/**",
                        "/api/**",
                        "/axiosTest/**",
                        "/static/**"
                ).permitAll()
                .antMatchers("/test/hasRole").hasRole("admin")
                .and();
//                .requiresChannel()
//                .and()
//                .portMapper()
//                .and();
        this.configureLogin(http);
//        this.configureAuthenticationProvider(http);
//        this.configureExceptionFilter(http);
//        this.configureAuthFilter(http);
//        this.configureAuthContextFilter(http);
        this.configureSecurityFilter(http);
//        this.configureOtherFilter(http);
        return http.build();
    }

    private HttpSecurity configureLogin(HttpSecurity loginConfig) throws Exception {
        FormLoginConfigurer<HttpSecurity> loginConfigurer = loginConfig.formLogin()
//                .loginPage("/page/user/login")
                .loginProcessingUrl("/user/login")
//                .successHandler(new FormAuthenticationSuccessHandler())
//                .failureHandler(new FormAuthenticationFailureHandler())
                .permitAll();
//        Field customLoginPage = ReflectionUtils.findField(FormLoginConfigurer.class, "customLoginPage");
//        ReflectionUtils.makeAccessible(customLoginPage);
//        customLoginPage.set(loginConfigurer, false);
        loginConfigurer
                .and()
//                .logout()
//                .and()
//                .passwordManagement()
                .userDetailsService(userDetailsService);
        userDetailsService.setPasswordEncoder(passwordEncoder());
        return loginConfig;

    }

    private HttpSecurity configureAuthenticationProvider(HttpSecurity providerConfig) {
//        filter.authenticationProvider()
//                .authenticationManager()
//                .and();
        return providerConfig;
    }

    private HttpSecurity configureExceptionFilter(HttpSecurity filterConfig) throws Exception {
//        filter.exceptionHandling()
//                .and();
        return filterConfig;
    }

    private HttpSecurity configureAuthFilter(HttpSecurity filterConfig) {
//        filter.addFilter()
//                .addFilterAt()
//                .addFilterBefore()
//                .addFilterAfter()
//        BasicAuthenticationFilter
//                .httpBasic()
//                .and()
        //RememberMeAuthenticationFilter
//                .rememberMe()
//                .and()
        //AnonymousAuthenticationFilter
//                .anonymous()
//                .and()
        //OAuth2AuthorizationRequestRedirectFilter
//                .oauth2Login()
//                .and()
        //OAuth2AuthorizationRequestRedirectFilter
        //OAuth2AuthorizationCodeGrantFilter授权码
//                .oauth2Client()
//                .and()
        //BearerTokenAuthenticationFilter
//                .oauth2ResourceServer()
//                .and()
        //X509AuthenticationFilter
//                .x509()
//                .and()
        //J2eePreAuthenticatedProcessingFilter
//                .jee()
//                .and();
        return filterConfig;
    }

    private HttpSecurity configureAuthContextFilter(HttpSecurity filterConfig) throws Exception {
//        SecurityContextHolderAwareRequestFilter
//        filter.servletApi()
//                .and()
        //SecurityContextPersistenceFilter/SecurityContextHolderFilter
//                .securityContext()
//                .and()
        //RequestCacheAwareFilter
//                .requestCache();
        return filterConfig;
    }

    private HttpSecurity configureSecurityFilter(HttpSecurity filterConfig) throws Exception {
        //CsrfFilter
        filterConfig.csrf().disable();
        //SessionManagementFilter
//                .sessionManagement()
//                .and();
        return filterConfig;
    }

    private HttpSecurity configureOtherFilter(HttpSecurity filterConfig) throws Exception {
        //CsrfFilter
        //HeaderWriterFilter
//        filter.headers()
//                .and();
        return filterConfig;
    }

}
